// 记录Promise的三种状态
const PENDING = "pending"
const FULFILLED = "fulfilled"
const REJECTED = "rejected"

/**
 * 运行一个微队列任务
 * 把传递的函数放到微队列中
 * @param {Function} callback
 */
function runMicroTask(callback) {
  // 判断node环境
  // 为了避免「变量未定义」的错误，这里最好加上前缀globalThis
  // globalThis是一个关键字，指代全局对象，浏览器环境为window，node环境为global
  if (globalThis.process && globalThis.process.nextTick) {
    process.nextTick(callback)
  } else if (globalThis.MutationObserver) {
    const ob = new MutationObserver(callback)
    const text = document.createTextNode("1")
    // 第二个参数对象必须设置一个属性为true，否则报错
    ob.observe(text, {
      // 监听的是节点的字符
      characterData: true
    })
    text.data = "2"
  } else {
    setTimeout(() => {
      callback()
    }, 0)
  }
}

/**
 * 判断一个数据是否是Promise对象
 * @param {any} obj
 * @returns
 */
function isPromise(obj) {
  return !!(obj && typeof obj === "object" && typeof obj.then === "function")
}

class MyPromise {
  /**
   * 创建一个Promise
   * @param {Function} executor 任务执行器，立即执行
   */
  constructor(executor) {
    this.PromiseState = PENDING // 状态
    this.PromiseResult = undefined // 数据
    this._handlers = [] // 处理函数形成的队列
    try {
      executor(this._resolve.bind(this), this._reject.bind(this))
    } catch (error) {
      this._reject(error)
    }
  }

  /**
   * 向处理队列中添加一个函数
   * @param {Function} executor 添加的函数
   * @param {String} state 该函数什么状态下执行
   * @param {Function} resolve 让then函数返回的Promise成功
   * @param {Function} reject 让then函数返回的Promise失败
   */
  _pushHandler(executor, state, resolve, reject) {
    this._handlers.push({
      executor,
      state,
      resolve,
      reject
    })
  }

  /**
   * 根据实际情况，执行队列
   */
  _runHandlers() {
    if (this.PromiseState === PENDING) {
      // 目前任务仍在挂起
      return
    }
    const iterator = this._handlers[Symbol.iterator]()
    let item = iterator.next()
    while (!item.done) {
      this._runOneHandler(item.value)
      item = iterator.next()
    }
    this._handlers = []
  }

  /**
   * 处理一个handler
   * @param {Object} handler
   */
  _runOneHandler({ executor, state, resolve, reject }) {
    runMicroTask(() => {
      if (this.PromiseState !== state) {
        // 状态不一致，不处理
        return
      }

      if (typeof executor !== "function") {
        // 传递后续处理并非一个函数
        this.PromiseState === FULFILLED
          ? resolve(this.PromiseResult)
          : reject(this.PromiseResult)
        return
      }
      try {
        // 这里接收参数的时候必须解构executor出来，因为这样this就没有问题，不然是指向一个对象
        const result = executor(this.PromiseResult)
        if (isPromise(result)) {
          result.then(resolve, reject)
        } else {
          resolve(result)
        }
      } catch (error) {
        reject(error)
      }
    })
  }

  /**
   * Promise A+规范的then
   * @param {Function} onResolved
   * @param {Function} onRejected
   */
  then(onResolved, onRejected) {
    return new MyPromise((resolve, reject) => {
      this._pushHandler(onResolved, FULFILLED, resolve, reject)
      this._pushHandler(onRejected, REJECTED, resolve, reject)
      this._runHandlers() // 执行队列
    })
  }

  /**
   * 仅处理失败的场景
   * @param {Function} onRejected
   */
  catch(onRejected) {
    return this.then(null, onRejected)
  }

  /**
   * 无论成功还是失败都会执行回调
   * @param {Function} onSettled
   */
  finally(onSettled) {
    return this.then(
      data => {
        onSettled()
        return data
      },
      reason => {
        onSettled()
        throw reason
      }
    )
  }

  /**
   * 更改任务状态
   * @param {String} newState 新状态
   * @param {any} value 相关数据
   */
  _changeState(newState, value) {
    if (this.PromiseState !== PENDING) {
      // 目前状态已经更改
      return
    }
    this.PromiseState = newState
    this.PromiseResult = value
    this._runHandlers() // 状态变化，执行队列
  }

  /**
   * 标记当前任务完成
   * @param {any} data 任务完成的相关数据
   */
  _resolve(data) {
    this._changeState(FULFILLED, data)
  }

  /**
   * 标记当前任务失败
   * @param {any} reason 任务失败的相关数据
   */
  _reject(reason) {
    this._changeState(REJECTED, reason)
  }

  /**
   * 返回一个已完成的Promise
   * 特殊情况：
   * 1. 传递的data本身就是ES6的Promise对象
   * 2. 传递的data是PromiseLike（Promise A+），返回新的Promise，状态和其保持一致即可
   * @param {any} data
   */
  static resolve(data) {
    if (data instanceof MyPromise) {
      return data
    }
    return new MyPromise((resolve, reject) => {
      if (isPromise(data)) {
        data.then(resolve, reject)
      } else {
        resolve(data)
      }
    })
  }

  /**
   * 得到一个被拒绝的Promise
   * @param {any}} reason
   */
  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason)
    })
  }

  /**
   * 得到一个新的Promise
   * 该Promise的状态取决于proms的执行
   * proms是一个可迭代对象，包含多个Promise
   * 全部Promise成功，则返回的Promise成功，数据为所有Promise成功的数据，并且顺序是按照传入的顺序排列
   * 只要有一个Promise失败，则返回的Promise失败，原因是第一个失败的Promise的原因
   * @param {iterable} proms
   */
  static all(proms) {
    return new MyPromise((resolve, reject) => {
      // try catch一下
      try {
        const results = []
        let count = 0 // Promise的总数
        let fulfilledCount = 0 // 已完成的数量
        // iterable对象都可以使用for of
        for (const p of proms) {
          let i = count
          count++
          // MyPromise.resolve(p)是将不是promise的转换为promise,统一处理
          MyPromise.resolve(p).then(data => {
            fulfilledCount++
            results[i] = data
            //ASD count是在同步的时候统计完成的，而fulfilledCount是在微任务队列排队累加的
            if (fulfilledCount === count) {
              // 当前是最后一个Promise完成了
              resolve(results)
            }
          }, reject)
        }
        // 如果这个可迭代对象没有能够迭代的，例如是[]，直接resolve([])
        if (count === 0) {
          resolve(results)
        }
      } catch (error) {
        reject(error)
      }
    })
  }

  /**
   * 等待所有的Promise有结果之后
   * 该方法返回的Promise完成
   * 并且按照顺序将所有结果汇总
   * @param {iterable} proms
   */
  static allSettled(proms) {
    const ps = []
    for (const p of proms) {
      ps.push(
        // 将非Promise转换为Promise，然后再处理，注意这里箭头函数return的是对象，有（）
        MyPromise.resolve(p).then(
          value => ({
            status: FULFILLED,
            value
          }),
          reason => ({
            status: REJECTED,
            reason
          })
        )
      )
    }
    // 借助all来实现
    return MyPromise.all(ps)
  }

  /**
   * 返回的Promise与第一个有结果的一致
   * @param {iterator} proms
   */
  static race(proms) {
    return new MyPromise((resolve, reject) => {
      for (const p of proms) {
        // 将非Promise转换为Promise，然后再处理
        MyPromise.resolve(p).then(resolve, reject)
      }
    })
  }
}
// 进行测试用的npm run test即可测试
// 将我们自己手写的Promise拷贝到一个单独的文件,并在底部加上
module.exports = {
  deferred() {
    const res = {}
    // 自己手写的Promise
    res.promise = new Promise((resolve, reject) => {
      // 内部将resolve和reject赋值上去
      res.resolve = resolve
      res.reject = reject
    })
    return res
  }
}
// 将我们自己手写的Promise拷贝到一个单独的文件,并在底部加上
module.exports = {
  deferred() {
    const res = {}
    // 自己手写的Promise
    res.promise = new Promise((resolve, reject) => {
      // 内部将resolve和reject赋值上去
      res.resolve = resolve
      res.reject = reject
    })
    return res
  }
}
